# coding=utf-8

"""
@Author: LiangChao
@Email: kevinleong1011@hotmail.com
@Desc: 
"""
import sys
import threading
import traceback
from datetime import datetime
from types import MethodType, FunctionType

from makit import fn

from .log import logger
from .message import todo, confirm, warning
from .utils import parse_test_info

RUNNING = 'running'
BLOCKED = 'blocked'
PASSED = 'passed'
ERROR = 'error'
FAILED = 'failed'
SKIPPED = 'skipped'


class TestScript:
    def __init__(self, func, project, context=None, fixture=None):
        self.context = context
        self.project = project
        self.test_func = func
        self.name, self.test_name, self.desc = parse_test_info(func)
        self.fixture = fixture
        self.start_time, self.finish_time, self.time_cost = None, None, 0
        self.messages = []
        self.status = None
        self.logger = logger
        self.outcome = None
        self.exception, self.error_summary, self.error_detail = None, None, None

    @property
    def marks(self):
        return getattr(self.test_func, 'marks', [])

    @property
    def id(self):
        return threading.current_thread().ident

    def has_marks(self, *marks):
        return all(map(lambda x: x in self.marks, marks))

    def warning(self, message):
        self.messages.append(warning(message))

    def todo(self, message):
        self.messages.append(todo(message))

    def confirm(self, message):
        self.messages.append(confirm(message))

    def parameterize(self, params):
        self.logger.info(f'Run with params: {params}')
        self.project.plugin.on_parameterized(self, self.context, params)

    def start_cycle(self, cycle):
        self.logger.info(f'-------------------- Cycle {cycle} --------------------')
        self.project.plugin.on_repeat_start(self, self.context, cycle)

    def finish_cycle(self, cycle):
        self.logger.info(f'---------------- Finish cycle {cycle} ------------------')
        self.project.plugin.on_repeat_finished(self, self.context, cycle)

    def start(self):
        self.status = RUNNING
        self.logger.debug(f'Start script: {self.test_name}')
        self.start_time = datetime.now()
        self.project.plugin.on_script_started(self, self.context)

    def finish(self):
        self.status = None
        self.finish_time = datetime.now()
        self.time_cost = (self.finish_time - self.start_time).seconds
        self.project.plugin.on_script_finished(self, self.context)
        self.logger.info(f'Finish test: {self.test_name}, cost {self.time_cost}s')
        logger.debug('-' * 50)

    def fail(self):
        self.outcome = FAILED
        self.project.plugin.on_script_failed(self, self.context)
        self.logger.error(f'Failed: {self.test_name}')
        self._catch_error()
        self.fixture.failed_count += 1

    def block(self):
        self.outcome = BLOCKED
        self.fixture.blocked_count += 1
        self.project.plugin.on_script_blocked(self, self.context)
        self.logger.debug(f'Blocked: {self.test_name}')

    def success(self):
        self.outcome = PASSED
        self.fixture.passed_count += 1
        self.project.plugin.on_script_passed(self, self.context)
        self.logger.success(f'Passed: {self.test_name}')

    def skip(self):
        self.outcome = SKIPPED
        self.fixture.skipped_count += 1
        self.project.plugin.on_script_skipped(self, self.context)
        self._catch_error()
        self.logger.success(f'Skipped: {self.test_name}')

    def error(self):
        self.project.plugin.on_script_error(self, self.context)
        self._catch_error()
        self.outcome = ERROR
        self.fixture.error_count += 1
        self.logger.error(f'Error: {self.test_name}')

    def _catch_error(self):
        e_type, e, b = sys.exc_info()
        self.exception = e
        self.error_summary = str(e)
        tb = traceback.format_exc()
        self.error_detail = tb
        self.logger.error(traceback.format_exc())

    def __call__(self, *args, **kwargs):
        cls = fn.get_class(self.test_func)
        if cls:
            instance = object.__new__(cls)
            fn.run(instance.__init__, *args, **kwargs)
            return fn.run(self.test_func, instance, *args, **kwargs)
        return fn.run(self.test_func, *args, **kwargs)

    def __eq__(self, other):
        if isinstance(other, TestScript):
            return self.test_func == other.test_func
        elif isinstance(other, (MethodType, FunctionType)):
            return self.test_func == other
        elif isinstance(other, str):
            return self.test_name == other
        else:
            return False

    def __repr__(self):
        return f'<TestScript {self.test_name}>'
